#ifndef _M_BINDING_H_
#define _M_BINDING_H_


#include"../mqcommon/mq_helper.hpp"
#include"../mqcommon/mq_logger.hpp"
#include"../mqcommon/mq_msg.pb.h"
#include<iostream>
#include<unordered_map>
#include<mutex>
#include<memory>

namespace xiang11mq
{
//定义绑定信息类
struct Binding
{
    using ptr = std::shared_ptr<Binding>;
    std::string exchange_name;
    std::string msgqueue_name;
    std::string binding_key;

    Binding() {}
    Binding(const std::string &ename, const std::string &qname, const std::string &key)
        :exchange_name(ename)
        ,msgqueue_name(qname)
        ,binding_key(key)
    {}
};
//队列与消息是一一对应的（因为是给某个交换机绑定队列， 因此一个交换机可能会有多个队列的绑定信息）
//消息队列名 与 绑定信息的映射关系
using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
//交换机与消息队列的映射关系
using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;


//定义绑定信息数据持久化类
class BindingMapper
{
public:
    BindingMapper(const std::string &dbfile)
        :_sql_helper(dbfile)   
    {
        std::string path = FileHelper::parentDirectory(dbfile);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        createTable();
    }
    //创建绑定信息表
    void createTable()
    {
        #define BINDING_CREATE_TABLE "create table if not exists binding_table(         \
            exchange_name varchar(32),                                          \
            msgqueue_name varchar(32),                                          \
            binding_key varchar(128)                                            \
        );"
        bool ret = _sql_helper.execute(BINDING_CREATE_TABLE, nullptr, nullptr);
        if(ret == false)
        {
            DLOG("创建消息队列表失败");
            abort();                    //异常退出
        }
    }
    //移除绑定信息表
    void removeTable()
    {
        #define BINDING_DROP_TABLE "drop table if exists binding_table;"
        bool ret = _sql_helper.execute(BINDING_DROP_TABLE, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除绑定信息表失败");
            abort(); // 异常退出
        }
    }
    //插入绑定信息
    bool insert(Binding::ptr &binding)
    {
        #define BINDING_INSERT_SQL "insert into binding_table values('%s', '%s', '%s');"
        char sql_str[255] = {0};
        sprintf(sql_str, BINDING_INSERT_SQL, binding->exchange_name.c_str(), binding->msgqueue_name.c_str(), binding->binding_key.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("新增绑定信息表数据失败");
            return false;
        }
        return true;
    }
    //删除绑定信息
    void remove(const std::string &ename, const std::string &qname)
    {
        #define BINDING_DELETE_SQL "delete from binding_table where exchange_name = '%s' and msgqueue_name = '%s';"
        char sql_str[128] = {0};
        sprintf(sql_str, BINDING_DELETE_SQL, ename.c_str(), qname.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("通过ename qname 删除绑定信息表数据失败");
        }
    }
    //删除一个交换机相关的绑定信息
    void removeExchangeBinding(const std::string &ename)
    {
        #define BINDING_DELETE_EXCHANGEBINDING_SQL "delete from binding_table where exchange_name = '%s';"
        char sql_str[128] = {0};
        sprintf(sql_str, BINDING_DELETE_EXCHANGEBINDING_SQL, ename.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("通过ename 删除绑定信息表数据失败");
        }
    }
    //删除一个消息队列相关的绑定信息
    void removeMsgQueueBinding(const std::string &qname)
    {
        #define BINDING_DELETE_MSGQUEUE_SQL "delete from binding_table where msgqueue_name = '%s';"
        char sql_str[128] = {0};
        sprintf(sql_str, BINDING_DELETE_MSGQUEUE_SQL, qname.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("通过qname 删除绑定信息表数据失败");
        }
    }
    //恢复绑定数据
    //队列跟绑定信息绝对是一一对应的
    //一个交换机可能绑定多个消息队列
    BindingMap recovery()
    {
        BindingMap result;
        #define BINDING_SELECT_SQL "select exchange_name, msgqueue_name, binding_key from binding_table;" 
        bool ret = _sql_helper.execute(BINDING_SELECT_SQL, selectCallback, &result);
        if(ret == 0)
        {
            ELOG("数据恢复失败");
            abort();
        }
        return result;
    }
private:
    static int selectCallback(void *arg, int numcol, char **row, char **fileds)
    {
        BindingMap *result = (BindingMap*) arg;
        auto bp = std::make_shared<Binding>();
        bp->exchange_name = row[0];
        bp->msgqueue_name = row[1];
        bp->binding_key = row[2];
        //为了防止 交换机相关的绑定信息已经存在 不能直接新建qmap 这样插入会覆盖
        //用引用 没有的话也能创建一个 有的话就能直接修改
        MsgQueueBindingMap &qmap = (*result)[bp->exchange_name];
        qmap.insert(std::make_pair(bp->msgqueue_name, bp));
        //qmap 是引用 不需要了
        //result->insert(std::make_pair(bp->exchange_name, qmap));
        return 0;       //必须有
    }
private:
    SqliteHelper _sql_helper;
};

//定义绑定信息数据管理类
class BindingManager
{
public:
    using ptr = std::shared_ptr<BindingManager>;
    BindingManager(const std::string &dbfile)
        :_mapper(dbfile)
    {
        _bindings = _mapper.recovery();
    }
    //添加绑定
    bool bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
    {
        //加锁 构造一个队列的绑定对象 添加映射关系
        std::unique_lock<std::mutex> lock(_mutex);

        auto it = _bindings.find(ename);
        //找到了交换机 还需在其与消息队列映射中找 qname
        if(it != _bindings.end() && it->second.find(qname) != it->second.end())
        {
            DLOG("要添加的绑定关系已存在");
            return true;
        }
        Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
        auto &qbmap = _bindings[ename];
        qbmap.insert(std::make_pair(qname, bp));
        //是否需要持久化 需要exchang 和 msgqueue 都持久化才行 去判断会增加耦合度
        //为了解耦合 直接参数加bool
        //持久化
        if(durable == true)
        {
            bool ret = _mapper.insert(bp);
            if(ret == false) return false;
        }
        return true;
    }
    //解绑
    void unbind(const std::string &ename, const std::string &qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if(it == _bindings.end() || it->second.find(qname) == it->second.end())
        {
            DLOG("要解绑的关系不存在！");
            return;
        }
        //直接删 有就删 没有也无所谓
        _mapper.remove(ename, qname);

        _bindings[ename].erase(qname);

    }
    //删除一个交换机的绑定信息
    void removeExchangeBindings(const std::string &ename)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeExchangeBinding(ename);
        _bindings.erase(ename);
    }
    //删除一个消息队列的绑定信息
    void removeMsgQueueBindings(const std::string &qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeMsgQueueBinding(qname);
        for(auto &[x, y] : _bindings)
        {
            //遍历 删除 消息队列qname
            y.erase(qname);
        }

    }
    //获取一个交换机的绑定信息
    MsgQueueBindingMap getExchangeBingdings(const std::string &ename)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if(it == _bindings.end())
        {
            DLOG("要查找的交换机绑定数据不存在")
            return MsgQueueBindingMap();
        }
        return it->second;
    }
    //获取指定绑定信息
    Binding::ptr getBinding(const std::string &ename, const std::string &qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if(it == _bindings.end() || it->second.find(qname) == it->second.end())
        {
            DLOG("要获取的绑定数据不存在")
            return Binding::ptr();
        }
        return it->second.find(qname)->second;
    }
    //查询某个交换机和消息队列是否绑定
    bool exists(const std::string &ename, const std::string &qname)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _bindings.find(ename);
        if (it == _bindings.end() || it->second.find(qname) == it->second.end())
        {
            DLOG("要查找的绑定数据不存在")
            return false;
        }
        return true;
    }
    //
    size_t size()
    {
        size_t total_size = 0;
        std::unique_lock<std::mutex> lock(_mutex);
        for(auto &[x, y] : _bindings)
        {
            //遍历 删除 消息队列qname
            total_size += y.size();
        }
        return total_size;
    }
    //
    void clear()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _bindings.clear();
    }
private:
    std::mutex _mutex;
    BindingMapper _mapper;      //持久化管理
    BindingMap _bindings;      //内存管理
};

}


#endif